/* wiringpi_php extension for PHP */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define SYSTEM_CMD_STR_LEN 128

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#include "php.h"
#include "ext/standard/info.h"
#include "php_wiringpi_php.h"
#include <wiringPi.h>

#include "can.h"
#include "core_functions.h"
#include "setup.h"
#include "serial_library.h"
#include "timing.h"
#include "spi_library.h"
#include "software_tone_library.h"
#include "software_pwm_library.h"
#include "shift_library.h"
#include "i2c_library.h"
#include "raspberry_pi_specifics.h"
#include "bcm_gpio.h"

#include <stdint.h>



//uint8_t  init = NO_ACTION;
//uint8_t  pull = NO_PULL;
//uint8_t  mode = MODE_READ;
//uint8_t  pin_number = NO_PIN;

//uint8_t i, len;
//uint8_t data, pin, debug_mode = DEBUG_OFF;


//int comparse(int argc, char **argv) {
//    int argnum, i, xmitnum;
//
//    if (argc < 2) {  // must have at least program name and len arguments
//                     // or -ie (GPIO_END) or -ib (GPIO_BEGIN)
//        fprintf(stderr, "Insufficient command line arguments\n");
//        return EXIT_FAILURE;
//    }
//
//    argnum = 1;
//    while (argnum < argc && argv[argnum][0] == '-') {
//
//        switch (argv[argnum][1]) {
//
//            case 'i':  // GPIO init
//                switch (argv[argnum][2]) {
//                    case 'b': init = GPIO_BEGIN; break;
//                    case 'e': init = GPIO_END; break;
//                    default:
//                        fprintf(stderr, "%c is not a valid init option\n", argv[argnum][2]);
//                        return EXIT_FAILURE;
//                }
//                break;
//
//            case 'd':  // Set/Clear/Read Mode
//                switch (argv[argnum][2]) {
//                    case 'r': mode = MODE_READ; break;
//                    case 's': mode = MODE_SET; break;
//                    case 'c': mode = MODE_CLR; break;
//                    case 'i': mode = MODE_INPUT_READ; break;
//                    default:
//                        fprintf(stderr, "%c is not a valid init option\n", argv[argnum][2]);
//                        return EXIT_FAILURE;
//                }
//                break;
//
//            case 'p':  // Pull up, down and no pull Mode
//                switch (argv[argnum][2]) {
//                    case 'u': pull = PULL_UP; break;
//                    case 'd': pull = PULL_DOWN; break;
//                    case 'n': pull = NO_PULL; break;
//		            default:
//                        fprintf(stderr, "%c is not a valid init option\n", argv[argnum][2]);
//                        return EXIT_FAILURE;
//                }
//                break;
//
//            case 'n':  // pin number
//	         pin_number = atoi(argv[argnum]+2);
//                 break;
//
//            case 'b':  // debug mode
//		 debug_mode = DEBUG_ON;
//                 break;
//
//            default:
//                fprintf(stderr, "%c is not a valid option\n", argv[argnum][1]);
//                return EXIT_FAILURE;
//        }
//
//        argnum++;   // advance the argument number
//
//    }
//
//    if (argnum == argc && init != NO_ACTION) // no further arguments are needed
//        return EXIT_SUCCESS;
//
//    return EXIT_SUCCESS;
//}

//*******************************************************************************
//  showusage: Print the usage statement and return errcode.
//*******************************************************************************

//int showusage(int errcode) {
//    printf("gpio \n");
//    printf("Usage: \n");
//    printf("  gpio [options]\n");
//    printf("\n");
//    printf("  Invoking gpio to set or reset a GPIO, enable disable pull up or pull down. Initialize or release a GPIO.\n");
//    printf("\n");
//    printf("  The following are the options, which must be a single letter\n");
//    printf("    preceded by a '-' and followed by another character.\n");
//    printf("    -ix where x is the GPIO init option, b[egin] or e[nd]\n");
//    printf("      The begin option must be executed before any transfer can happen.\n");
//    printf("      The end option will return the GPIO to inputs and turn off all pull up and pull downs.\n");
//    printf("      It may be included with a transfer.\n");
//    printf("    -dx where x is 'c' for clear, 's' is for set, 'r' for read and 'i' for read and set as input.\n");
//    printf("    -px where x is the GPIO pull up or down option. 'u' for pull up, 'd' for pull down and 'n' for none.\n");
//    printf("    -nx where x is the pin number.\n");
//    printf("\n");
//    return errcode;
//}




// to be done
// http://wiringpi.com/reference/priority-interrupts-and-threads/

/* For compatibility with older PHP versions */
#ifndef ZEND_PARSE_PARAMETERS_NONE
#define ZEND_PARSE_PARAMETERS_NONE() \
	ZEND_PARSE_PARAMETERS_START(0, 0) \
	ZEND_PARSE_PARAMETERS_END()
#endif




/* {{{ void wiringpi_php_test1()
 */
PHP_FUNCTION(wiringpi_php_test1)
{
//int argc1;
//char **argv1;
//    printf("Running ... \n");
//    // parse the command line
////    if (comparse(argc1, argv1) == EXIT_FAILURE) return showusage (EXIT_FAILURE);
//
//    if (!bcm2835_init()) return 1;
//
//    // GPIO begin if specified
//    if (init == GPIO_BEGIN) ;
//
//
//    // If len is 0, no need to continue, but do GPIO end if specified
//    // if (len == 0) {
//    //     if (init == GPIO_END) ;
//    //	 printf("Zero length ... error!\n");
//    //     return EXIT_SUCCESS;
//    // }
//    pin_number = 15;
//    switch (pin_number) {
//            case 3:
//	       pin = RPI_V2_GPIO_P1_03;
//	    break;
//            case 5:
//	       pin = RPI_V2_GPIO_P1_05;
//	    break;
//            case 7:
//	       pin = RPI_V2_GPIO_P1_07;
//	    break;
//            case 26:
//	       pin = RPI_V2_GPIO_P1_26;
//	    break;
//            case 24:
//	       pin = RPI_V2_GPIO_P1_24;
//	    break;
//            case 21:
//	       pin = RPI_V2_GPIO_P1_21;
//	    break;
//            case 19:
//	       pin = RPI_V2_GPIO_P1_19;
//	    break;
//            case 23:
//	       pin = RPI_V2_GPIO_P1_23;
//	    break;
//            case 10:
//	       pin = RPI_V2_GPIO_P1_10;
//	    break;
//            case 11:
//	       pin = RPI_V2_GPIO_P1_11;
//	    break;
//            case 12:
//	       pin = RPI_V2_GPIO_P1_12;
//	    break;
//            case 13:
//	       pin = RPI_V2_GPIO_P1_13;
//	    break;
//            case 15:
//	       pin = RPI_V2_GPIO_P1_15;
//	    break;
//            case 16:
//	       pin = RPI_V2_GPIO_P1_16;
//	    break;
//            case 18:
//	       pin = RPI_V2_GPIO_P1_18;
//	    break;
//            case 22:
//	       pin = RPI_V2_GPIO_P1_22;
//	    break;
//	    default:
//	      pin = NO_PIN;
//    }
//
//    switch (pull) {
//    	    case PULL_UP:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_UP);
//	    break;
//    	    case PULL_DOWN:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_DOWN);
//	    break;
//    	    case NO_PULL:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_OFF);
//	    break;
//    	    default:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_OFF);
//    }
//
//mode =  MODE_SET;
//    switch (mode)
//    {
//    	case MODE_READ:
//	        data = bcm2835_gpio_lev(pin);
//	        printf("Reading pin: %d\n", data);
//	        break;
//    	case MODE_INPUT_READ:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
//	        data = bcm2835_gpio_lev(pin);
//	        printf("Reading pin: %d\n", data);
//	        break;
//    	case MODE_SET:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
//	        bcm2835_gpio_set(pin);
//	        break;
//    	case MODE_CLR:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
//	        bcm2835_gpio_clr(pin);
//	        break;
//	    default:
//	        printf("Wrong mode ...!\n");
//    }
//
//    if (debug_mode == DEBUG_ON) {
//    	printf("Init %d\n", init);
//    	printf("Mode %d\n", mode);
//    	printf("Pull %d\n", pull);
//    	printf("Pin Number %d\n", pin_number);
//    	printf("Pin %d\n", pin);
//    }
//
//    if (init == GPIO_END) gpio_reset();
//    bcm2835_close();
//    printf("... done!\n");
//
//	php_printf("The extension %s is loaded and working!\r\n", "wiringpi_php");
}



PHP_FUNCTION(wiringpi_php_test2)
{
//int argc1;
//char **argv1;
//    printf("Running ... \n");
//    // parse the command line
////    if (comparse(argc1, argv1) == EXIT_FAILURE) return showusage (EXIT_FAILURE);
//
//    if (!bcm2835_init()) return 1;
//
//    // GPIO begin if specified
//    if (init == GPIO_BEGIN) ;
//
//
//    // If len is 0, no need to continue, but do GPIO end if specified
//    // if (len == 0) {
//    //     if (init == GPIO_END) ;
//    //	 printf("Zero length ... error!\n");
//    //     return EXIT_SUCCESS;
//    // }
//    pin_number = 15;
//    switch (pin_number) {
//            case 3:
//	       pin = RPI_V2_GPIO_P1_03;
//	    break;
//            case 5:
//	       pin = RPI_V2_GPIO_P1_05;
//	    break;
//            case 7:
//	       pin = RPI_V2_GPIO_P1_07;
//	    break;
//            case 26:
//	       pin = RPI_V2_GPIO_P1_26;
//	    break;
//            case 24:
//	       pin = RPI_V2_GPIO_P1_24;
//	    break;
//            case 21:
//	       pin = RPI_V2_GPIO_P1_21;
//	    break;
//            case 19:
//	       pin = RPI_V2_GPIO_P1_19;
//	    break;
//            case 23:
//	       pin = RPI_V2_GPIO_P1_23;
//	    break;
//            case 10:
//	       pin = RPI_V2_GPIO_P1_10;
//	    break;
//            case 11:
//	       pin = RPI_V2_GPIO_P1_11;
//	    break;
//            case 12:
//	       pin = RPI_V2_GPIO_P1_12;
//	    break;
//            case 13:
//	       pin = RPI_V2_GPIO_P1_13;
//	    break;
//            case 15:
//	       pin = RPI_V2_GPIO_P1_15;
//	    break;
//            case 16:
//	       pin = RPI_V2_GPIO_P1_16;
//	    break;
//            case 18:
//	       pin = RPI_V2_GPIO_P1_18;
//	    break;
//            case 22:
//	       pin = RPI_V2_GPIO_P1_22;
//	    break;
//	    default:
//	      pin = NO_PIN;
//    }
//
//    switch (pull) {
//    	    case PULL_UP:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_UP);
//	    break;
//    	    case PULL_DOWN:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_DOWN);
//	    break;
//    	    case NO_PULL:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_OFF);
//	    break;
//    	    default:
//		bcm2835_gpio_set_pud(pin, BCM2835_GPIO_PUD_OFF);
//    }
//
//    mode =  MODE_CLR;
//    switch (mode)
//    {
//    	case MODE_READ:
//	        data = bcm2835_gpio_lev(pin);
//	        printf("Reading pin: %d\n", data);
//	        break;
//    	case MODE_INPUT_READ:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
//	        data = bcm2835_gpio_lev(pin);
//	        printf("Reading pin: %d\n", data);
//	        break;
//    	case MODE_SET:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
//	        bcm2835_gpio_set(pin);
//	        break;
//    	case MODE_CLR:
//	        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
//	        bcm2835_gpio_clr(pin);
//	        break;
//	    default:
//	        printf("Wrong mode ...!\n");
//    }
//
//    if (debug_mode == DEBUG_ON) {
//    	printf("Init %d\n", init);
//    	printf("Mode %d\n", mode);
//    	printf("Pull %d\n", pull);
//    	printf("Pin Number %d\n", pin_number);
//    	printf("Pin %d\n", pin);
//    }
//
//    if (init == GPIO_END) gpio_reset();
//    bcm2835_close();
//    printf("... done!\n");

}








/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(wiringpi_php)
{
#if defined(ZTS) && defined(COMPILE_DL_WIRINGPI_PHP)
	ZEND_TSRMLS_CACHE_UPDATE();
#endif

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(wiringpi_php)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "wiringpi_php support", "enabled");
	php_info_print_table_end();
}
/* }}} */

/* {{{ arginfo
 */
ZEND_BEGIN_ARG_INFO(arginfo_wiringpi_php_test1, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_wiringpi_php_test2, 0)
	ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()



PHP_MINIT_FUNCTION(wiringpi_php)
{
    REGISTER_LONG_CONSTANT("GPIO_INPUT",   INPUT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_OUTPUT",   OUTPUT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("PWM_OUTPUT",   PWM_OUTPUT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_CLOCK",   GPIO_CLOCK, CONST_CS | CONST_PERSISTENT);


    REGISTER_LONG_CONSTANT("GPIO_PUD_UP",   PUD_UP, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_PUD_DOWN",  PUD_DOWN, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_PUD_OFF",   PUD_OFF, CONST_CS | CONST_PERSISTENT);


    REGISTER_LONG_CONSTANT("GPIO_HIGH",  HIGH, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_LOW",   LOW, CONST_CS | CONST_PERSISTENT);


    REGISTER_LONG_CONSTANT("PWM_MODE_BAL",  PWM_MODE_BAL, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("PWM_MODE_MS",   PWM_MODE_MS, CONST_CS | CONST_PERSISTENT);


    REGISTER_LONG_CONSTANT("GPIO_MODE_READ",   MODE_READ, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_MODE_SET",   MODE_SET, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_MODE_CLR",   MODE_CLR, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("GPIO_MODE_INPUT_READ",   MODE_INPUT_READ, CONST_CS | CONST_PERSISTENT);

    can_minit(module_number);
}




/* }}} */

/* {{{ wiringpi_php_functions[]
 */
static const zend_function_entry wiringpi_php_functions[] = {
	PHP_FE(wiringpi_php_test1,		arginfo_wiringpi_php_test1)
	PHP_FE(wiringpi_php_test2,		arginfo_wiringpi_php_test2)


	PHP_FE(wiringPiSetup,		    arginfo_wiringPiSetup)
	PHP_FE(wiringPiSetupGpio,		arginfo_wiringPiSetupGpio)
	PHP_FE(wiringPiSetupPhys,		arginfo_wiringPiSetupPhys)
	PHP_FE(wiringPiSetupSys,		arginfo_wiringPiSetupSys)
    



	PHP_FE(wpiPinMode,		arginfo_wpiPinMode)
	PHP_FE(wpiPullUpDnControl,		arginfo_wpiPullUpDnControl)
	PHP_FE(wpiDigitalWrite,		arginfo_wpiDigitalWrite)
	PHP_FE(wpiPwmWrite,		arginfo_wpiPwmWrite)
	PHP_FE(wpiDigitalRead,		arginfo_wpiDigitalRead)
	PHP_FE(wpiAnalogRead,		arginfo_wpiAnalogRead)
	PHP_FE(wpiAnalogWrite,		arginfo_wpiAnalogWrite)


    PHP_FE(digitalWriteByte, arginfo_digitalWriteByte) 
    PHP_FE(pwmSetMode, arginfo_pwmSetMode) 
    PHP_FE(pwmSetRange, arginfo_pwmSetRange)
    PHP_FE(pwmSetClock, arginfo_pwmSetClock)
    PHP_FE(piBoardRev, arginfo_piBoardRev)
    PHP_FE(wpiPinToGpio, arginfo_wpiPinToGpio)
    PHP_FE(physPinToGpio, arginfo_physPinToGpio)
    PHP_FE(setPadDrive, arginfo_setPadDrive)

    PHP_FE(millis, arginfo_millis)
    PHP_FE(micros, arginfo_micros)
    PHP_FE(delay, arginfo_delay)
    PHP_FE(delayMicroseconds, arginfo_delayMicroseconds)


    PHP_FE(softToneCreate, arginfo_softToneCreate)
    PHP_FE(softToneWrite, arginfo_softToneWrite)

    PHP_FE(softPwmCreate, arginfo_softPwmCreate)
    PHP_FE(softPwmWrite, arginfo_softPwmWrite)

    PHP_FE(shiftIn, arginfo_shiftIn)
    PHP_FE(shiftOut, arginfo_shiftOut)


    PHP_FE(serialOpen, arginfo_serialOpen)
    PHP_FE(serialClose, arginfo_serialClose)
    PHP_FE(serialPutchar, arginfo_serialPutchar)
    PHP_FE(serialPuts, arginfo_serialPuts)
    PHP_FE(serialPrintf, arginfo_serialPrintf)
    PHP_FE(serialDataAvail, arginfo_serialDataAvial) 
    PHP_FE(serialGetchar, arginfo_serialGetchar) 
    PHP_FE(serialFlush, arginfo_serialFlush) 

    PHP_FE(wiringPiSPISetup, arginfo_wiringPiSPISetup)
    PHP_FE(wiringPiSPIDataRW, arginfo_wiringPiSPIDataRW)


    PHP_FE(wiringPiI2CSetup, arginfo_wiringPiI2CSetup)
    PHP_FE(wiringPiI2CRead, arginfo_wiringPiI2CRead)
    PHP_FE(wiringPiI2CWrite, arginfo_wiringPiI2CWrite)
    PHP_FE(wiringPiI2CWriteReg8, arginfo_wiringPiI2CWriteReg8)
    PHP_FE(wiringPiI2CWriteReg16, arginfo_wiringPiI2CWriteReg16)
    PHP_FE(wiringPiI2CReadReg8, arginfo_wiringPiI2CReadReg8)
    PHP_FE(wiringPiI2CReadReg16, arginfo_wiringPiI2CReadReg16)

    PHP_FE(can_init, arginfo_can_init)
    PHP_FE(can_open, arginfo_can_open)
    PHP_FE(can_send, arginfo_can_send)
    PHP_FE(can_read, arginfo_can_read)
    PHP_FE(can_close, arginfo_can_close)
    PHP_FE(can_uninit, arginfo_can_uninit)

    PHP_FE(gpio_bcm_pin_ctrl, arginfo_gpio_bcm_pin_ctrl)
    PHP_FE(gpio_bcm_pin_reset, arginfo_gpio_bcm_pin_reset)
   
	PHP_FE_END
};
/* }}} */

/* {{{ wiringpi_php_module_entry
 */
zend_module_entry wiringpi_php_module_entry = {
	STANDARD_MODULE_HEADER,
	"wiringpi_php",					/* Extension name */
	wiringpi_php_functions,			/* zend_function_entry */
	PHP_MINIT(wiringpi_php),							/* PHP_MINIT - Module initialization */
	NULL,							/* PHP_MSHUTDOWN - Module shutdown */
	PHP_RINIT(wiringpi_php),			/* PHP_RINIT - Request initialization */
	NULL,							/* PHP_RSHUTDOWN - Request shutdown */
	PHP_MINFO(wiringpi_php),			/* PHP_MINFO - Module info */
	PHP_WIRINGPI_PHP_VERSION,		/* Version */
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_WIRINGPI_PHP
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(wiringpi_php)
#endif



